#include "openmove_Prefix.h"
#include "SceneBase.h"
#include "OgreEngine.h"
#include "ExampleFrameListener.h"
#include "SkeletalAnimationFrameListener.h"
#include "OgreExternalTextureSourceManager.h"
#include "OgreExternalTextureSource.h"
#include "SceneWater.h"
#include "WaterMesh.h"

static Vector3 s_CameraStart(0, 0, -200.f);

#define POSITIONS_SIZE 4

CSceneWater :: CSceneWater(COgreEngine &Creator, const String & Name, const int pos) : CSceneBase(Creator, Name, pos), m_pPluie(NULL)
{
}

CSceneWater :: ~CSceneWater(void)
{
}

void CSceneWater :: Create(TCreatorEntities const &CreatorEntities, TCreatorNodes const &CreatorNodes, TCreatorLights const &CreatorLights, ENetConfig IsClient, Vector3 &CameraStartPos)
{
	CSceneBase::Create(CreatorEntities, CreatorNodes, CreatorLights, IsClient, CameraStartPos);

	m_pWaterMesh = new WaterMesh("WaterMesh", 3000.0f, 128);

	TCreatorEntities WaterEntities;
	TCreatorNodes WaterNodes;
	WaterEntities.push_back(SEntity("WaterMesh", "WaterMesh"));
	WaterNodes.push_back(SNode("WaterMesh", "WaterMesh"));
	AddGeometries(WaterEntities, WaterNodes);

	Entity *pEntity = m_OgreEngine.m_pCurrentSceneManager->getEntity("WaterMesh");
	pEntity->setMaterialName("Examples/Water0");
	Node *pNode = m_pSceneNode->getChild("WaterMesh");
	pNode->setPosition(-1500, 5, -1500);

	Entity *pLoup = m_OgreEngine.m_pCurrentSceneManager->getEntity("farfale");
	SkeletonInstance *pSkel = pLoup->getSkeleton();
	m_pLeftFoot = pSkel->getBone("ATE_LeftFoot");
	m_pRightFoot = pSkel->getBone("ATE_RightFoot");

	//m_OgreEngine.m_Root.addFrameListener(this);
	//m_FrameListeners.push_back(this);

	m_pWaterMesh->PARAM_C = 0.3f ; // ripple speed 
	m_pWaterMesh->PARAM_D = 0.4f ; // distance
	m_pWaterMesh->PARAM_U = 0.05f ; // viscosity
	m_pWaterMesh->PARAM_T = 0.13f ; // time
	m_pWaterMesh->useFakeNormals = true;

	m_pInputDevice = PlatformManager::getSingleton().createInputReader();
	m_pInputDevice->initialise(m_OgreEngine.m_pCurrentRenderWindow, true, true);

    m_rttTex = m_OgreEngine.m_Root.getRenderSystem()->createRenderTexture( "WaterRttReflectTex", 1024, 1024, TEX_TYPE_2D, PF_R8G8B8 );
	{
		m_ReflectCam = m_OgreEngine.m_pCurrentSceneManager->createCamera("WaterReflectCam");
        m_ReflectCam->setNearClipDistance(m_pCurrentCamera->getNearClipDistance());
        m_ReflectCam->setFarClipDistance(m_pCurrentCamera->getFarClipDistance());
        m_ReflectCam->setAspectRatio(m_pCurrentCamera->getAspectRatio());
		m_ReflectCam->setFOVy(m_resetParams.m_FOVy);

        Viewport *v = m_rttTex->addViewport( m_ReflectCam );
        v->setClearEveryFrame( true );
        v->setBackgroundColour( ColourValue::White );

        //MaterialPtr mat = MaterialManager::getSingleton().create("RttReflectMat",
        //    ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

		//mat->setAmbient(0, 0, 0);
		//mat->setDiffuse(0, 0, 0, 1);
		MaterialPtr mat = MaterialManager::getSingleton().getByName("Examples/Water0");
		pWaterPass = mat->getTechnique(0)->getPass(0);
		ColourValue tmp = pWaterPass->getAmbient();
		tmp = pWaterPass->getDiffuse();
		tmp = pWaterPass->getSelfIllumination();

		TextureUnitState *t = pWaterPass->createTextureUnitState("WaterRttReflectTex");
		t->setColourOperation(LBO_MODULATE);
		t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
		t->setProjectiveTexturing(true, m_ReflectCam);

		MovablePlane * mPlane = new MovablePlane("WaterReflectPlane");
        mPlane->d = 0;
        mPlane->normal = Vector3::UNIT_Y;

		// set up linked reflection
        m_ReflectCam->enableReflection(mPlane);
        // Also clip
        m_ReflectCam->enableCustomNearClipPlane(mPlane);

	
        SceneNode *mPlaneNode = m_pSceneNode->createChildSceneNode();

		SceneNode *node = m_OgreEngine.m_pCurrentSceneManager->getSceneNode("WaterMesh");
		node->attachObject(mPlane);

		// Give the plane a texture
		m_PlaneEnt = m_OgreEngine.m_pCurrentSceneManager->getEntity("WaterMesh");
		//m_PlaneEnt->setMaterialName("RttReflectMat");
	}
}

bool CSceneWater :: frameStarted(const FrameEvent& evt)
{
	CSceneBase::frameStarted(evt);


	if (m_pCameraFrameListener->mInputDevice->isKeyDown(KC_V) && m_pCameraFrameListener->mTimeUntilNextToggle <= 0)
	{
		fakeAddEvent(SOpenMoveDisplayPacket::e_FAE_PushWave, 0.3, String("0.0001"));
		m_pCameraFrameListener->mTimeUntilNextToggle = 0.0f;
	} 


	static Real headDepth = 2.0f;

	//void animateHead(Real timeSinceLastFrame)
	{
		Real timeSinceLastFrame = evt.timeSinceLastFrame;
		// sine track? :)
		static double sines[4] = {0.0f,100.0f,200.0f,300.0f};
		static const double adds[4] = {0.3f,-1.6f,1.1f,0.5f};
		static Vector3 oldPos = Vector3::UNIT_Z;
		static Vector3 oldPos2 = Vector3::UNIT_Z;
		for(int i=0;i<4;i++) {
			sines[i]+=adds[i]*timeSinceLastFrame;
		}
		Real tx = ((sin(sines[0]) + sin(sines[1])) / 4.0f + 0.5 ) * (float)(128.0f-2.0f) + 1.0f ;
		Real ty = ((sin(sines[2]) + sin(sines[3])) / 4.0f + 0.5 ) * (float)(128.0f-2.0f) + 1.0f ;
		//m_pWaterMesh->push(tx,ty, -headDepth);
		Real step = 3000.0f / 128.0f ;
		{
		//headNode->resetToInitialState();
		//headNode->scale(3,3,3);
		Vector3 newPos = Vector3(step*tx, headDepth, step*ty);
		Vector3 diffPos = newPos - oldPos ;
		//Quaternion headRotation = Vector3::UNIT_Z.getRotationTo(diffPos);
		oldPos = newPos ;
		//headNode->translate(newPos);
		//headNode->rotate(headRotation);
		}
	}


	{
		Real x = m_pLeftFoot->getWorldPosition().x / 32.0f + 64.0f - 0.5f;
		Real z = m_pLeftFoot->getWorldPosition().z / 32.0f + 64.0f - 0.5f;
		m_pWaterMesh->push(
			x,
			z,
			(m_LastLeftFoot - m_pLeftFoot->getWorldPosition().y) / 5.0f
			);
		m_LastLeftFoot = m_pLeftFoot->getWorldPosition().y;
	}
	{
		Real x = m_pRightFoot->getWorldPosition().x / 32.0f + 64.0f - 0.5f;
		Real z = m_pRightFoot->getWorldPosition().z / 32.0f + 64.0f - 0.5f;
		//m_pWaterMesh->push(
		//	x,
		//	z,
		//	(m_LastRightFoot - m_pRightFoot->getWorldPosition().y) / 5.0f
		//	);
		m_LastRightFoot = m_pRightFoot->getWorldPosition().y;
	}

	m_OgreEngine.setDebugText(
		StringConverter::toString(m_pRightFoot->getWorldPosition()) + "\n"
		);

	m_pWaterMesh->updateMesh(evt.timeSinceLastFrame);

	m_pInputDevice->capture();
	Real changeSpeed = evt.timeSinceLastFrame ;
	
	// adjust keyboard speed with SHIFT (increase) and CONTROL (decrease)
	if (m_pInputDevice->isKeyDown(KC_LSHIFT) || m_pInputDevice->isKeyDown(KC_RSHIFT)) {
		changeSpeed *= 10.0f ;
	}
	if (m_pInputDevice->isKeyDown(KC_LCONTROL)) { 
		changeSpeed /= 10.0f ;
	}

#define ADJUST_RANGE(_value,_keyPlus,_keyMinus,_minVal,_maxVal,_change) {\
	if (m_pInputDevice->isKeyDown(_keyPlus)) \
		{ _value+=_change ; if (_value>=_maxVal) _value = _maxVal ; } ; \
	if (m_pInputDevice->isKeyDown(_keyMinus)) \
		{ _value-=_change; if (_value<=_minVal) _value = _minVal ; } ; \
}

		ADJUST_RANGE(headDepth, KC_U, KC_J, 0.0f, 10.0f, 0.5*changeSpeed) ;

		ADJUST_RANGE(m_pWaterMesh->PARAM_C, KC_2, KC_1, 0.0f, 10.0f, 0.1f*changeSpeed) ;

		ADJUST_RANGE(m_pWaterMesh->PARAM_D, KC_4, KC_3, 0.1f, 10.0f, 0.1f*changeSpeed) ;

		ADJUST_RANGE(m_pWaterMesh->PARAM_U, KC_6, KC_5, -2.0f, 10.0f, 0.1f*changeSpeed) ;

		ADJUST_RANGE(m_pWaterMesh->PARAM_T, KC_8, KC_7, 0.0f, 10.0f, 0.1f*changeSpeed) ;





	m_ReflectCam->setPosition(m_pCurrentCamera->getPosition());
	m_ReflectCam->setOrientation(m_pCurrentCamera->getOrientation());

	{
		Real curTime = m_Timer.getSeconds();
		Real const start = 30.0f;
		if (curTime > start)
		{
			Real val = ((curTime - start)/ (m_resetParams.m_duration - m_transitionOut.m_duration)) * (1 / 0.9f);
			pWaterPass->setAmbient(1.0f, 0.5f, 0.5f);
			pWaterPass->setDiffuse(1.0f, 1.0f - val, 1.0f - val, 1.0f);

			if (NULL != m_pPluie)
			{
				ParticleEmitter *emitter = m_pPluie->getEmitter(0);
				if (NULL != emitter)
				{
					emitter->setEmissionRate(10000.0f * val);
				}
			}
		}
		else
		{
			pWaterPass->setAmbient(1.0f, 0.5f, 0.5f);
			pWaterPass->setDiffuse(1.0f, 1.0f, 1.0f, 1.0f);
		}
	}

/*
	Light *pLight = findLight(String("farfale-pointLight"));
	if (NULL != pLight)
	{
		pLight->setDiffuseColour(0.5f, 0.5f - (val / 2.0f), 0.5f - (val / 2.0f));

		m_OgreEngine.setDebugText(
			"LightDiffuse: " + StringConverter::toString(pLight->getDiffuseColour()) + "\n" +
			"WaterDiffuse: " + StringConverter::toString(pWaterPass->getDiffuse()));
	}
*/
	return (true);
}

// render target events
void CSceneWater :: preRenderTargetUpdate(const RenderTargetEvent& evt)
{
    // Hide plane 
    m_PlaneEnt->setVisible(false);

}
void CSceneWater :: postRenderTargetUpdate(const RenderTargetEvent& evt)
{
    // Show plane 
    m_PlaneEnt->setVisible(true);
}

void CSceneWater :: Detach(void)
{
	CSceneBase::Detach();
	m_rttTex->removeListener(this);

	m_pSceneNode->removeAndDestroyChild("SceneWaterPluie1");
	m_OgreEngine.m_pCurrentSceneManager->destroyParticleSystem(m_pPluie);
	m_pPluie = NULL;
}

void CSceneWater :: Attach(void)
{
	CSceneBase::Attach();

	//m_pCurrentCamera->setPosition(318.3, 626.1, 275.8);
	//Quaternion orientation;
	//orientation.w = 0.750834;
	//orientation.x = -0.213314;
	//orientation.y = -0.601297;
	//orientation.z = -0.170830;
	//m_pCurrentCamera->setOrientation(orientation);
	//m_pCurrentCamera->rotate(Vector3(0, 1, 0), Radian(-1.979f));
	//m_pCurrentCamera->setDirection(0, -1, 0);
	//m_OgreEngine.m_pViewPort->setBackgroundColour(ColourValue(0.0f, 0.0f, 0.0f, 1.0f));
	//m_OgreEngine.m_pCurrentSceneManager->setSkyBox(true, "SkyBox/Test");

	m_rttTex->addListener(this);

	assert(NULL == m_pPluie);
	m_pPluie = m_OgreEngine.m_pCurrentSceneManager->createParticleSystem("SceneWaterPluie1", "pluie1");
	SceneNode *pPluieNode = m_pSceneNode->createChildSceneNode("SceneWaterPluie1");
	pPluieNode->attachObject(m_pPluie);
	m_pPluie->setCastShadows(false);
}

void CSceneWater :: processEvents(void)
{
	CSceneBase::processEvents();

	bool found = false;

	m_TimeToIdle = 1.0f;
	Real speed = 0.0f;

	for (TEvents::iterator It = m_Events.begin(); It != m_Events.end(); ++It)
	{
		switch (It->m_Packet.eventid)
		{
			case SOpenMoveDisplayPacket::e_FAE_PushWave:
			{
				found = true;
				Real timer = m_Timer.getSeconds();
				if (It->m_IsFirstTime)
				{
					Vector3 tmp = s_CameraStart;
#if 0
					tmp.x += (Math::RangeRandom(0, 1) - 0.5) * 3000.0f;
					tmp.z += (Math::RangeRandom(0, 1) - 0.5) * 3000.0f;
#else
					if (It->m_Packet.user_position_h > 0 && It->m_Packet.user_position_v > 0)
					{
						tmp.x -= (It->m_Packet.user_position_h - 0.5) * 3000.0f;
						tmp.z -= (It->m_Packet.user_position_v - 0.5) * 1000.0f;
					}
#endif
					if (m_Positions.size() > POSITIONS_SIZE - 1)
					{
						m_Positions.erase(m_Positions.begin());
					}
					if (tmp != s_CameraStart)
					{
						m_Positions.push_back(tmp);
					}
				}
				if (m_Positions.size() > 3)
				{
					m_TimeToIdle = (1.0f / It->m_Packet.duree) * It->m_pTimer->getSeconds();
				}
				speed = It->m_Packet.user_speed;
				break;
			}
		}
	}

	if (m_Positions.size() > 3 && true == found)
	{
		Vector3 val = Utils::linearInterpolate(m_Positions[2], m_Positions[3], m_TimeToIdle);
		//Vector3 val = Utils::cosineInterpolate(m_Positions[2], m_Positions[3], m_TimeToIdle);
		//Vector3 val = Utils::cubicInterpolate(m_Positions[0], m_Positions[1], m_Positions[2], m_Positions[3], m_TimeToIdle);

		Real x = val.x / 32.0f + 64.0f - 0.5f;
		Real z = val.z / 32.0f + 64.0f - 0.5f;

		m_pWaterMesh->push(x, z, -2.0f * speed);
	}
}